#!/usr/bin/env python
# -*-*- encoding: utf-8 -*-*-
#
# Copyright (C) 2008 Håvard Gulldahl.
#
# Licensed under the GNU Public License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.gnu.org/licenses/gpl-2.0.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os.path, urllib, logging

from PyQt4 import QtCore


import gdata

import gdata.contacts.service as service
import gdata.contacts as contacts
import gcontacts.gcontactsbase as gcontactsbase


if False:
    import httplib
    httplib.HTTPConnection.debuglevel=1  ## verbosely output http traffic

import atom
atom.XML_STRING_ENCODING=None # workaround bug

class ContactsWorkerThread(QtCore.QThread):
  def __init__(self, agent, *args):
    QtCore.QThread.__init__(self, *args)
    self.agent = agent

  def doGetFeed(self, uri):
    logging.debug("GetFeed: %s", uri)
    try:
      return self.agent.contactsclient.Get(uri, converter=gcontactsbase.ContactsFeedFromString)
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      return False

class ContactsLoginThread(ContactsWorkerThread):
  def setCredentials(self, username, password):
    self.username = username
    self.password = password

  def run(self):
    logging.debug("Logging in by thread %s", QtCore.QThread.currentThread())
    try:
      self.agent.contactsclient.ClientLogin(self.username, self.password)
    except Exception, e:
      logging.debug(e)
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      self.agent.emit(QtCore.SIGNAL('loggedIn(PyQt_PyObject)'), False)
    else:
      self.agent.authenticated = True
      logging.debug("logged in")
      self.agent.emit(QtCore.SIGNAL('loggedIn(PyQt_PyObject)'), True)
    return
    self.exec_()

class ContactsGetContactsThread(ContactsWorkerThread):
  feedUri = '/m8/feeds/contacts/default/full'
  #'base?sortorder=descending&orderby=lastmodified&max-results=35'
  urlData = { 'sortorder': 'descending', 'orderby': 'lastmodified'}

  def setRange(self, startindex=1, maxresults=100):
    self.urlData['start-index'] = startindex
    self.urlData['max-results'] = maxresults

  def run(self):
    try:
      uri = self.feedUri + '?' + urllib.urlencode(self.urlData)
      feed = self.doGetFeed(uri)
      self.agent.emit(QtCore.SIGNAL('gotContactsFeed(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
        feed, self.urlData['start-index'], self.urlData['max-results'])
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), str(e.message))
    return
    self.exec_()

class ContactsGetGroupsThread(ContactsWorkerThread):
  urlData = { 'sortorder': 'descending', 'orderby': 'lastmodified'}
  def run(self):
    try:
      #uri = self.feedUri + '?' + urllib.urlencode(self.urlData)
      feed = self.agent.contactsclient.GetGroupsFeed()
      #logging.debug('got groups feed %s', feed)
      self.agent.emit(QtCore.SIGNAL('gotGroupsFeed(PyQt_PyObject)'), feed)
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), str(e.message))
    return
    self.exec_()


class ContactsDeleteThread(ContactsWorkerThread):
  def setContact(self, contact):
    self.contact = contact
  def run(self):
    try:
      self.agent.contactsclient.DeleteContact(self.contact.GetEditLink().href)
      self.agent.emit(QtCore.SIGNAL('deleted(PyQt_PyObject)'), self.contact)
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
    return
    self.exec_()

class ContactsDeleteContactsThread(ContactsWorkerThread):
  def setContacts(self, contacts):
    self.contacts = contacts
  def run(self):
    try:
      feed = gdata.BatchFeed()
      for contact in self.contacts:
        job = gdata.BatchEntry(atom_id=atom.Id(text=contact.id.text), batch_operation=gdata.BatchOperation(op_type=gdata.BATCH_DELETE),
          batch_id=gdata.BatchId(text=contact.id.text), link=[contact.GetEditLink(),])
        feed.AddBatchEntry(job)
      response = self.agent.contactsclient.ExecuteBatch(feed,
                    service.DEFAULT_BATCH_URL)
      logging.debug([ "%s:%s" % (e.batch_id.text, e.batch_status.reason) for e in response.entry])
      deletedids = [e.batch_id.text for e in response.entry]
      deleted = filter(lambda x: x.id.text in deletedids, self.contacts)
      self.agent.emit(QtCore.SIGNAL('deletedContacts(PyQt_PyObject)'), deleted)
    except Exception, e:
      logging.exception(e)
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), str(e.message))
    return
    self.exec_()

class ContactsGetPhotoThread(ContactsWorkerThread):
  def setUri(self, uri):
    self.uri = uri
  def run(self):
    try:
      data = self.agent.contactsclient.GetMedia(self.uri).file_handle.read()
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      return False
    else:
      self.agent.emit(QtCore.SIGNAL('gotPhoto(PyQt_PyObject)'), data)
    return
    self.exec_()

class ContactsSearchThread(ContactsWorkerThread):
  def setQuery(self, scope, query):
    self.query = query
    self.kwargs = {}
    if scope=="user":
      self.method = self.agent.contactsclient.SearchUserPhotos
    elif scope=="contact":
      self.method = self.agent.contactsclient.SearchContactsPhotos
    elif scope=="public":
      self.method = self.agent.contactsclient.SearchCommunityPhotos
    else:
      self.method = self.agent.contactsclient.SearchUserPhotos
      self.kwargs['user'] = scope

  def run(self):
    try:
      photos = self.method(self.query, **self.kwargs)
    except Exception, e:
      logging.debug(e.message)
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      return False
    else:
      self.agent.emit(QtCore.SIGNAL('gotSearchResult(PyQt_PyObject)'), photos )
    return
    self.exec_()


class ContactsUpdateThread(ContactsWorkerThread):
  pass

class ContactsInsertThread(ContactsWorkerThread):
  def setData(self, **kwords):
    self.kwords = kwords

  def run(self):
    try:
      metadata = gdata.photos.PhotoEntry()
      metadata.title=atom.Title(text=self.kwords['title'])
      if self.kwords['summary']:
        metadata.summary = atom.Summary(text=self.kwords['summary'], summary_type='text')
      if self.kwords['keywords']:
        metadata.media.keywords = gdata.media.Keywords(text=self.kwords['keywords'])
      if self.kwords['location']:
        metadata.geo.set_location(self.kwords['location'])
      logging.debug(metadata)
      self.agent.contactsclient.InsertPhoto(self.albumItem.entry, metadata,
        self.kwords['filename_or_handle'], self.kwords['content_type'])
      self.agent.emit(QtCore.SIGNAL('insertedPhoto()'))
      logging.debug("Uploaded successfully")
      self.agent.doGetPhotos(self.albumItem)
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      raise
    return
    self.exec_()

  def run2(self):
    try:
      self.kwords.pop('location', None)
      self.agent.contactsclient.InsertPhotoSimple(self.albumItem.entry, **self.kwords)
      self.agent.emit(QtCore.SIGNAL('insertedPhoto()'))
      logging.debug("Uploaded successfully")
      self.agent.doGetPhotos(self.albumItem)
    except Exception, e:
      self.agent.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      raise
    return
    self.exec_()

class ContactsAgent(QtCore.QObject):
  contactsclient = None
  authenticated = False
  defaultFormat = 'image/jpeg'

  def __init__(self):
    QtCore.QObject.__init__(self)
    self.contactsclient = service.ContactsService(source='lurtgjort.no-gcontacts-0.5')
    self.login = ContactsLoginThread(self)
    self.getContacts = ContactsGetContactsThread(self)
    self.getGroups = ContactsGetGroupsThread(self)
    self.delete = ContactsDeleteThread(self)
    self.deleteContacts = ContactsDeleteContactsThread(self)
    self.getPhoto = ContactsGetPhotoThread(self)
    self.search = ContactsSearchThread(self)
    self.update = ContactsUpdateThread(self)
    self.insert = ContactsInsertThread(self)

  def doAbort(self):
    logging.debug('aborting all threads!')
    for thr in (self.login, self.getAlbums, self.delete, self.getThumbnail, self.getPhoto, self.search, self.update.self.insert):
      logging.debug("thread is runing: %s", thr.isRunning())
      if thr.isRunning():
        thr.terminate()

  def doLogin(self, un, pw):
    self.login.setCredentials(un, pw)
    self.login.start()

  def doGetContacts(self, start=1, count=100):
    logging.debug("dogetcontacts")
    self.getContacts.setRange(start, count)
    self.getContacts.start()

  def doGetFeed(self, uri):
    logging.debug("doGetFeed: %s", uri)
    try:
      return self.contactsclient.GetContactsFeed(uri)
    except Exception, e:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
      return False

  def doGetGroups(self):
    logging.debug("doGetGroups")
    self.getGroups.start()

  def doDeleteContact(self, contact):
    logging.debug("doDeleteContact %s", contact.title.text)
    if not self.authenticated:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), 'Not logged in')
      return
    self.delete.setContact(contact)
    self.delete.start()

  def doDeleteContacts(self, contacts):
    logging.debug("doDeleteContacts %s", len(contacts))
    if not self.authenticated:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), 'Not logged in')
      return
    self.deleteContacts.setContacts(contacts)
    self.deleteContacts.start()

  def doGetPhoto(self, url):
    logging.debug("dogetPhoto %s", url)
    if self.getPhoto.isRunning():
      logging.debug("is already running!")
      self.getPhoto.terminate()
    self.getPhoto.setUri(url)
    self.getPhoto.start()

  def doInsert(self, albumItem, filename, name=None, format=None):
    logging.debug("doInsertPhoto %s", filename)
    if not self.authenticated:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), 'Not logged in')
      return False

    try:
      fhandle = open(filename)
    except IOError, e:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), 'File does not exist: %s' % filename)
      return
    if name is None:
      name = os.path.basename(filename)
    if format is None:
      format = self.defaultFormat
    keywords = ['picasapusher',]
    tags = Tagreader(filename)
    keywords += tags.getKeywords()

    logging.debug("Inserting image")
    self.insertPhoto.setData(albumItem, title=name, summary=tags.getCaption(), filename_or_handle=fhandle, content_type=format, keywords=','.join(keywords), location=tags.getLocation())
    self.insertPhoto.start()

  def doSearch(self, scope, query):
    logging.debug("doSearch %s: %s", scope, query)
    if scope in ('user','contact') and not self.authenticated:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), 'Not logged in')
      return False

    if self.search.isRunning():
      logging.debug("aborting running search!")
      self.search.terminate()
    self.search.setQuery(scope, query)
    self.search.start()

  def doUpdatePhoto(self, entry):
    logging.debug("doUpdatePhoto, %s", entry)
    try:
      newEntry = self.contactsclient.UpdatePhotoMetadata(entry)
    except Exception, e:
      self.emit(QtCore.SIGNAL('error(PyQt_PyObject)'), e.message)
    else:
      self.emit(QtCore.SIGNAL('gotUpdatedPhoto(PyQt_PyObject)'), newEntry )

  def abort(self):
    logging.debug("abort!")